#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "request.h"
#include "log.h"

/*
 * [GET|POST] [PATH] [HTTP 1* / 1*]
 */
static int parse_first_line(connection *connect)
{
	char *cur, *front;
	size_t length;
	char buffer[DEFAULT_LIMIT_REQUEST_LINE+2];
	if((length = tcp_readline(connect->client, buffer, DEFAULT_LIMIT_REQUEST_LINE+1)) == 0)
	{
		logger(PRIORITY_WARN, "read 0 size from remote connection. at %d, in %s.", __LINE__, __FILE__);
		return HTTP_BAD_REQUEST;
	}

	if(length == DEFAULT_LIMIT_REQUEST_LINE)
		return HTTP_REQUEST_URI_TOO_LARGE;
	cur = buffer;
	
	/* get method. */
	connect->request.method = http_method_code(cur);
	if(connect->request.method == -1)
		return HTTP_METHOD_NOT_ALLOWED;

	/* only GET and POST suported. */
	if(connect->request.method != HTTP_METHOD_GET &&
			connect->request.method != HTTP_METHOD_POST)
		return HTTP_NOT_IMPLEMENTED;

	/* forward cur to path */
	while(*cur != SP && *cur != '\0') cur++;
	if(*cur == '\0')
			return HTTP_BAD_REQUEST;

	/* if http://[host] get. remove it for me. */
	if(0 == strncasecmp(++cur, "http://", 7))
	{
		if((cur = strchr(cur + 7, '/')) == NULL)
			return HTTP_BAD_REQUEST;
	}
	front = cur;

	/* forward cur to the end of path. */
	while(*cur != SP && *cur != '?' && *cur != '\0') cur++;
	if(*cur == '\0')
		return HTTP_BAD_REQUEST;

	/* get path */
	connect->request.path = (char*)malloc(cur-front + 1);
	memcpy(connect->request.path, front, cur-front);
	connect->request.path[cur-front] = '\0';

	/* if query exist, get it. */
	if(*cur == '?')
	{
		front = ++cur;
		while(*cur != SP && *cur != '\0') cur++;
		if(*cur == '\0')
			return HTTP_BAD_REQUEST;

		connect->request.content_length = cur - front;
		connect->request.content = (char*)malloc(connect->request.content_length + 1);
		memcpy(connect->request.content, front, connect->request.content_length);
		connect->request.content[connect->request.content_length] = '\0';
	}

	/* forward to "HTTP 1.1" */
	if(*cur != SP)
		return HTTP_BAD_REQUEST;

	if(strncmp(++cur, "HTTP/", 5) != 0)
		return HTTP_BAD_REQUEST;

	cur += 5;
	if(*cur != '1')
		return HTTP_VERSION_NOT_SUPPORTED;
	cur++;
	if(*cur != '.')
		return HTTP_BAD_REQUEST;
	cur++;
	if(atoi(cur) == 1) {
		connect->request.version = HTTP_VERSION_1_1;
		return HTTP_OK;
	}

	if(atoi(cur) == 0) {
		connect->request.version = HTTP_VERSION_1_0;
		return HTTP_OK;
	}

	return HTTP_BAD_REQUEST;
}

static int parse_header_fields(connection *connect)
{
	char *cur, *tail;
	size_t length;
	char buffer[DEFAULT_LIMIT_REQUEST_FIELDSIZE+2];
	http_pair *pair;

parse_next:
	if((length = tcp_readline(connect->client, buffer, DEFAULT_LIMIT_REQUEST_FIELDSIZE+1)) == 0)
	{
		logger(PRIORITY_WARN, "read 0 size from remote connection. at %d, in %s.", __LINE__, __FILE__);
		return HTTP_BAD_REQUEST;
	}

	if(length == DEFAULT_LIMIT_REQUEST_FIELDSIZE)
		return HTTP_REQUEST_ENTITY_TOO_LARGE;

	/* get blank line. at header finish. */
	if(length == 1 || length == 2)
		return HTTP_OK;

	if((cur = strchr(buffer, ':')) == NULL)
		return HTTP_UNPROCESSABLE_ENTITY;

	*cur = '\0';
	if(cur - buffer > DEFAULT_LIMIT_REQUEST_FIELDS)
		return HTTP_REQUEST_ENTITY_TOO_LARGE;

	tail = cur++ - 1;
	while(*tail == SP || *tail == HT)
	{
		*tail = '\0';
		 tail --;
	}

	while(*cur == SP || *cur == HT)
	{
		*cur = '\0';
		 cur ++;
	}

	pair = new_pair(buffer, cur);
	if(connect->request.entry == NULL)
		connect->request.entry = pair;

	http_pair_push(&(connect->request.tail), pair);
	goto parse_next;
}

int do_request_process(connection *connect)
{
	int status, length;
	char* content;
	http_pair *pair;
	status = parse_first_line(connect);
	if(!is_HTTP_SUCCESS(status))
		return status;

	status = parse_header_fields(connect);
	if(!is_HTTP_SUCCESS(status))
		return status;

	pair = http_pair_find(HTTP_HEADER_CONNECTION, connect->request.entry);
	connect->client->keep_alive = (pair && strcasecmp(pair->value, "Keep-Alive") == 0) ? 1 : 0;

	if(connect->request.method == HTTP_METHOD_GET )
		return HTTP_OK;

	if(connect->request.method == HTTP_METHOD_POST)
	{
		pair = http_pair_find(HTTP_HEADER_CONTENT_LENGTH, connect->request.entry);
		if(pair == NULL)
			return HTTP_LENGTH_REQUIRED;

		if((length = atoi(pair->value)) == 0)
			return HTTP_OK;
		if(connect->request.content == NULL) {
			connect->request.content = (char*)malloc(length + 1);
			if(tcp_read(connect->client, connect->request.content, length) == -1) {
				free(connect->request.content);
				connect->request.content = NULL;
				return HTTP_INTERNAL_SERVER_ERROR;
			}
			connect->request.content_length = length;
			connect->request.content[length] = '\0';
			return HTTP_OK;
		}

		content = (char*)malloc(length + connect->request.content_length + 2);
		strncpy(content, connect->request.content, connect->request.content_length);
		content[connect->request.content_length] = '&';
		if(tcp_read(connect->client, connect + connect->request.content_length + 1, length) == -1) {
			free(content);
			return HTTP_INTERNAL_SERVER_ERROR;
		}

		connect->request.content_length += length + 1;
		connect->request.content[connect->request.content_length] = '\0';
		return HTTP_OK;
	}
	
	return status;
}

